import {FrameBuffer} from './FrameBuffer'
import {ref} from 'vue'


const WS_PROJ_FRAME = 1             // cmd[8], x[16], y[16], len[24], ... data
const WS_PROJ_AUDIO_INIT = 2        // cmd[8], sampleRate[16], bit[8], channels[8]
const WS_PROJ_AUDIO_FRAME = 3       // cmd[8], ... data


export class Poster {

    // public frameBuffer = new FrameBuffer

    public addr = (localStorage.getItem("proj-addr") || "192.168.4.1").trim()

    public audioSetting = {
        sampleRate: 22050 ,
        bits: 16 ,
        channels: 1 ,
    }

    private ws: WebSocket|null = null
    private audioRecorder: any = null

    // 视频帧率
    public fps = ref(0)
    private fps_frames = 0

    // 音频码率
    public bitrate = ref(0)
    private bitrate_bytes = 0

    public audio_post_times = ref(0)
    private _audio_post_times = 0

    private sonic: any
    public volume = ref(0.5)

    public connStatus : 'disconnected'|'connecting'|'connected' = 'disconnected'
    public continuousPost = false

    constructor(public frameBuffer:FrameBuffer) {

        setInterval(()=>{
            this.fps.value = this.fps_frames || 0
            this.fps_frames = 0

            this.bitrate.value = (this.bitrate_bytes||0) * this.audioSetting.bits * this.audioSetting.channels
            this.bitrate_bytes = 0

            this.audio_post_times.value = this._audio_post_times
            this._audio_post_times = 0

        },1000)
    }

    setAddr(ip:string) {
        this.addr = ip
    }

    setAudioRecorder(recorder){
        this.audioRecorder=recorder
    }
    
    postAudioSetting = () => {

        if(!this.ws || !this.audioRecorder) {
            console.error("no ws or audioRecorder")
            return
        }

        // cmd[8], sampleRate[16], bits[8], channels[8]
        let data = [
            WS_PROJ_AUDIO_INIT ,
            (this.audioSetting.sampleRate>>8) & 255 ,
            this.audioSetting.sampleRate & 255 ,
            this.audioSetting.bits & 255 ,
            this.audioSetting.channels & 255 ,
        ]
        let ab = (new Uint8Array(data)).buffer
        this.ws.send(ab)
    }

    // 根据字节数计算播放时长（ms）
    samplesToMS(sampleBytes) {
        return sampleBytes / (this.audioSetting.sampleRate*this.audioSetting.channels) * 1000
    }

    postAudioFrame = () => {
        if(!this.ws || !this.audioRecorder) {
            return
        }
        if(!this.audioRecorder.buffers.length) {
            setTimeout(this.postAudioFrame, 100)
            return ;
        }

        // resample 
        let pcm = (window as any).Recorder.SampleData(
                this.audioRecorder.buffers
                , this.audioRecorder.srcSampleRate
                , this.audioSetting.sampleRate
            )
            
        // 不小于 200 ms
        // if(this.samplesToMS(pcm.data.length)<200) {
        //     setTimeout(this.postAudioFrame, 100)
        //     // console.log(this.samplesToMS(pcm.data.length), pcm.data.length)
        //     return ;
        // }
        

        this.bitrate_bytes+= (pcm.data.length as number)*2
        this._audio_post_times ++

        // 清理 this.audioRecorder 缓冲区里的数据
        this.audioRecorder.buffers.length = 0

        let dataFrm = new Uint8Array(pcm.data.length*2 + 1)
        dataFrm.set([WS_PROJ_AUDIO_FRAME as any], 0)
        dataFrm.set(new Uint8Array(pcm.data.buffer), 1);


        // console.log(">>>",this.samplesToMS(pcm.data.length), pcm.data.length)
        this.ws.send(dataFrm.buffer)
    }

    samplesSaveAsFile() {

        let pcm = (window as any).Recorder.SampleData(this.audioRecorder.buffers,this.audioRecorder.srcSampleRate,this.audioSetting.sampleRate)
        console.log(pcm)

        const blob = new Blob([pcm.data], { type: ".pcm" });

        const downloadUrl = URL.createObjectURL(blob)
        const a = document.createElement('a')
        a.href = downloadUrl
        a.download = "audio.pcm"
        document.body.appendChild(a)
        a.click()
        URL.revokeObjectURL(downloadUrl)
    }

    async videoFrameSaveAsFile() {
        
        let [img] = this.frameBuffer.popVideoFrame()
        if(!img) {
            return
        }

        const blob = new Blob([img], { type: "frame.bmp" });

        const downloadUrl = URL.createObjectURL(blob)
        const a = document.createElement('a')
        a.href = downloadUrl
        a.download = "frame.bmp"
        document.body.appendChild(a)
        a.click()
        URL.revokeObjectURL(downloadUrl)
    }

    postVideoFrame = async () => {
        if(!this.ws) {
            return
        }

        let [ab,x,y] = this.frameBuffer.popVideoFrame()
        while(!ab) {
            [ab,x,y] = await this.frameBuffer.nextFrame()
        }
        
        if(x<0||y<0) {
            console.error("x=",x,"y=",y)
            return
        }
        let ua = new Uint8Array(ab.byteLength+8)
        ua[0] = WS_PROJ_FRAME
        // x
        ua[1] = (x>>8) & 255
        ua[2] = x & 255
        // y
        ua[3] = (y>>8) & 255
        ua[4] = y & 255
        // length
        ua[5] = (ab.byteLength >> 16) & 255
        ua[6] = (ab.byteLength >> 8) & 255
        ua[7] = ab.byteLength & 255
        ua.set(new Uint8Array(ab), 8)

        // console.log(x,y,ua.buffer)
        this.ws && this.ws.send(ua.buffer)
    }

    startProjection(ip:string) {

        let url = `ws://${ip}:8022/projection`

        console.log(url)
        if(this.ws) {
            this.ws.close()
        }

        this.ws = new WebSocket(url);
        // window.ws = this.ws
        // this.ws.binaryType = "arraybuffer"
        
        this.ws.onopen = () => {
            console.log(">>>> Display Connection has opened"); 
            this.connStatus = "connected"
            if(!this.ws) return
            // this.ws.send(new Uint8Array([WS_DISP_CMD_REFRESH]));

            if(this.audioRecorder) {

                this.audioRecorder.open(()=>{//打开这个流
                    console.log("record.start()")
                    this.audioRecorder.start()
                },console.error)

                setTimeout(this.postAudioSetting, 0)
            }
            setTimeout(this.postVideoFrame, 50)

            //
            this.frameBuffer.needKeyframe = true
        }
        this.ws.onclose = () => {
            this.connStatus = "disconnected"
            console.log("RTC Connection closed.");
            this.ws = null
        }
        this.ws.addEventListener('error', (event) => {
            console.log('WebSocket error: ', event)
            this.ws?.onclose && this.ws.onclose(event as any)
        })

        this.ws.onmessage = (evt) => {
            let rspnData = evt.data.trim()
            let rspn = JSON.parse(rspnData)

            if(rspn.media=='video') {
                if(rspn.act=="frame" && rspn.msg=='ok' ) {
                    this.fps_frames ++   
                    if(this.continuousPost) {
                        this.postVideoFrame()
                    }
                }
            }
            else if(rspn.media=='audio') {
                
                console.log("rspn.act == ", rspn.act)

                if(rspn.act=='frame') {
                    this.postAudioFrame()
                }
                else if (rspn.act=='init') {
                    if(rspn.msg=='ok') {
                        this.postAudioFrame()
                    }
                }
            }
        }
        
        this.connStatus = "connecting"
    }

    stopProjection() {
        if(this.ws) {
            try{
                this.ws.close()
            }catch(e) {
                console.log(e)
            }
            this.ws = null
        }
        this.connStatus = "disconnected"
    }

    projection(continuous=true) {
        
        localStorage.setItem("proj-addr", this.addr)

        this.continuousPost = continuous
        
        if(this.ws) {
            this.postVideoFrame()
        }
        else {
            this.startProjection(this.addr)
        }
    }

}

